<?php

declare(strict_types=1);

namespace app\util;

use Hashids\Hashids as HashidsService;
use think\facade\Log;

/**
 * ID加密/解密服务（静态类）
 * 
 * 用于将数字ID转换为短小、唯一、非连续的字符串，以及将字符串转换回原始ID
 * 适用于URL缩短、ID混淆等场景
 */
class HashUtil
{
    /**
     * @var array Hashids实例缓存
     */
    protected static $instances = [];

    /**
     * @var array 默认配置信息
     */
    protected static $defaultConfig = [
        'default' => [
            'salt' => 'app_hash_id_salt',
            'length' => 8,
            'alphabet' => 'abcdefhijklmnpqrstuvwxy0123456789'
        ]
    ];

    /**
     * 获取Hashids实例
     * 
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return HashidsService
     */
    public static function getInstance(string $type = 'default', array $config = []): HashidsService
    {
        $key = $type . md5(serialize($config));

        if (!isset(self::$instances[$key])) {
            // 合并配置
            $mergedConfig = !empty($config)
                ? $config
                : (self::$defaultConfig[$type] ?? self::$defaultConfig['default']);

            self::$instances[$key] = new HashidsService(
                $mergedConfig['salt'],
                $mergedConfig['length'],
                $mergedConfig['alphabet']
            );
        }

        return self::$instances[$key];
    }

    /**
     * 加密ID
     * 
     * @param int $id 要加密的ID
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return string 加密后的字符串
     */
    public static function encode(int $id, string $type = 'default', array $config = []): string
    {
        try {
            $instance = self::getInstance($type, $config);
            return $instance->encode($id);
        } catch (\Exception $e) {
            Log::error('HashIdService encode error: ' . $e->getMessage(), json_encode([
                'id' => $id,
                'type' => $type,
                'config' => $config
            ]));
            return '';
        }
    }

    /**
     * 加密ID为纯数字
     * 
     * @param int $id 要加密的ID
     * @return string 加密后的数字字符串
     */
    public static function encodeNumeric(int $id): string
    {
        return self::encode($id, 'numeric');
    }

    /**
     * 批量加密ID
     * 
     * @param array $ids 要加密的ID数组
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return string 加密后的字符串
     */
    public static function encodeBatch(array $ids, string $type = 'default', array $config = []): string
    {
        try {
            $instance = self::getInstance($type, $config);
            return $instance->encode(...$ids);
        } catch (\Exception $e) {
            Log::error('HashIdService encodeBatch error: ' . $e->getMessage(), json_encode([
                'ids' => $ids,
                'type' => $type,
                'config' => $config
            ]));
            return '';
        }
    }

    /**
     * 解密ID
     * 
     * @param string $hash 加密后的字符串
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return array 解密后的ID数组
     */
    public static function decode(string $hash, string $type = 'default', array $config = []): array
    {
        try {
            $instance = self::getInstance($type, $config);
            return $instance->decode($hash);
        } catch (\Exception $e) {
            Log::error('HashIdService decode error: ' . $e->getMessage(), json_encode([
                'hash' => $hash,
                'type' => $type,
                'config' => $config
            ]));
            return [];
        }
    }

    /**
     * 解密纯数字ID
     * 
     * @param string $hash 加密后的数字字符串
     * @return array 解密后的ID数组
     */
    public static function decodeNumeric(string $hash): array
    {
        return self::decode($hash, 'numeric');
    }

    /**
     * 解密单个ID
     * 
     * @param string $hash 加密后的字符串
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return int|null 解密后的ID，如果解密失败则返回null
     */
    public static function decodeSingle(string $hash, string $type = 'default', array $config = []): ?int
    {
        $decoded = self::decode($hash, $type, $config);
        return !empty($decoded) ? $decoded[0] : null;
    }

    /**
     * 解密单个纯数字ID
     * 
     * @param string $hash 加密后的数字字符串
     * @return int|null 解密后的ID，如果解密失败则返回null
     */
    public static function decodeSingleNumeric(string $hash): ?int
    {
        return self::decodeSingle($hash, 'numeric');
    }

    /**
     * 批量解密ID
     * 
     * @param array $hashes 加密后的字符串数组
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return array 解密后的结果数组
     */
    public static function decodeBatch(array $hashes, string $type = 'default', array $config = []): array
    {
        $results = [];
        foreach ($hashes as $hash) {
            $results[$hash] = self::decode($hash, $type, $config);
        }
        return $results;
    }

    /**
     * 验证Hash是否有效
     * 
     * @param string $hash 加密后的字符串
     * @param string $type 实例类型
     * @param array $config 配置信息
     * @return bool 是否有效
     */
    public static function isValid(string $hash, string $type = 'default', array $config = []): bool
    {
        return !is_null(self::decodeSingle($hash, $type, $config));
    }

    /**
     * 验证纯数字Hash是否有效
     * 
     * @param string $hash 加密后的数字字符串
     * @return bool 是否有效
     */
    public static function isValidNumeric(string $hash): bool
    {
        return self::isValid($hash, 'numeric');
    }

    /**
     * 获取当前默认配置
     * 
     * @return array 配置信息
     */
    public static function getConfig(): array
    {
        return self::$defaultConfig;
    }

    /**
     * 设置默认配置
     * 
     * @param array $config 配置信息
     * @param string $type 配置类型
     */
    public static function setConfig(array $config, string $type = 'default'): void
    {
        self::$defaultConfig[$type] = array_merge(
            self::$defaultConfig[$type] ?? [],
            $config
        );
        // 清除实例缓存
        self::$instances = [];
    }

    /**
     * 清除实例缓存
     */
    public static function clearCache(): void
    {
        self::$instances = [];
    }
}
